package br.com.foxinline.generico;

import br.com.foxinline.modelo.Anexo;
import br.com.foxinline.modelo.Usuario;
import br.com.foxinline.servico.AnexoServico;
import br.com.foxinline.util.ArquivoUtilitario;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

/**
 *
 * @author elyjr
 */
public class ServicoGenerico<T> {

    @PersistenceContext
    private EntityManager entityManager;
    private Class<T> entity;
    private ArquivoUtilitario arquivoUtilitario = new ArquivoUtilitario();
    @EJB
    AnexoServico anexoServico;
    private final ArrayList<String> metodos;

    public ServicoGenerico(Class<T> entity) {
        this.entity = entity;

        metodos = new ArrayList<String>();
        metodos.add("getAnexos");
        metodos.add("getAnexo");
        metodos.add("getCroqui");
        metodos.add("getAnnex");
        metodos.add("getAnnexCroquis");
        metodos.add("getLogo");
        metodos.add("getLogoRegularizacaoFundiaria");
    }

    public void save(T entity) {
        try {
            Class p[] = new Class[1];
            p[0] = Date.class;

            //data criação
            Method meth = this.entity.getSuperclass().getMethod("setDataCriacao", p);

            Object arglist[] = new Object[1];
            arglist[0] = new Date();

            meth.invoke(entity, arglist);

            p = new Class[1];
            p[0] = Usuario.class;

            meth = this.entity.getSuperclass().getMethod("setUsuarioCriacao", p);
            arglist = new Object[1];
            arglist[0] = verifySystemUserForLogin();

            meth.invoke(entity, arglist);

        } catch (Exception e) {
//            Logger.getLogger(ServicoGenerico.class.getName()).log(Level.SEVERE, null, e);
        }

        entityManager.persist(entity);
        gravarAnexo(entity);
    }

    public void delete(T entity) {

        try {

            Class p[] = new Class[1];
            p[0] = Boolean.class;

            //SET ATIVO = FALSE
            Method meth = this.entity.getSuperclass().getMethod("setAtivo", p);

            Object arglist[] = new Object[1];
            arglist[0] = Boolean.FALSE;

            meth.invoke(entity, arglist);

        } catch (Exception e) {

            Logger.getLogger(ServicoGenerico.class.getName()).log(Level.SEVERE, null, e);
        }

        update(entity);

//        T entityToBeRemoved = entityManager.merge(entity);
//        entityManager.remove(entityToBeRemoved);
    }

    public void update(T entity) {
        try {
            Class p[] = new Class[1];
            p[0] = Date.class;

            //DATE UPDATE
            Method meth = this.entity.getSuperclass().getMethod("setDataAtualizacao", p);

            Object arglist[] = new Object[1];
            arglist[0] = new Date();

            meth.invoke(entity, arglist);

            //USUARIO UPDATE
            p = new Class[1];
            p[0] = Usuario.class;
            meth = this.entity.getSuperclass().getMethod("setUsuarioAtualizacao", p);

            Object arglist1[] = new Object[1];
            arglist1[0] = verifySystemUserForLogin();

            meth.invoke(entity, arglist1);

        } catch (Exception e) {

            Logger.getLogger(ServicoGenerico.class.getName()).log(Level.SEVERE, null, e);
        }
        entityManager.merge(entity);
        entityManager.flush();
    }

    public void updateAnexos(T entity) {
        gravarAnexo(entity);
    }

    public T find(Long entityID) {
        return entityManager.find(entity, entityID);
    }

    public List<T> findAll() {
        return entityManager.createQuery("select e from " + entity.getSimpleName() + " e").getResultList();
    }

    public EntityManager getEntityManager() {
        return entityManager;
    }

    public Long quantidadeRegistros() {
        Long result = (Long) entityManager.createQuery("select count(e) from " + entity.getSimpleName() + " e").getSingleResult();

        return result != null ? result : 0;
    }

    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public List<T> find(T t, Integer first, Integer pageSize) {
        String sql = "select e from " + entity.getSimpleName() + " e ";

        Query query = entityManager.createQuery(sql).setFirstResult(first).setMaxResults(pageSize);

        return query.getResultList();
    }

    /**
     * Resgata usuário logado no sistema
     *
     * @return
     */
    public Usuario verifySystemUserForLogin() {
        try {
            String nome = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal().getName();

            if (nome != null && !nome.isEmpty()) {
                Usuario usr;

                final String sql = "select u from " + Usuario.class.getSimpleName() + " u where "
                        + "u.login like :nome and u.ativo = true";
                Query query = getEntityManager().createQuery(sql);
                query.setParameter("nome", nome);
                usr = (Usuario) query.getSingleResult();

                return usr;
            }
        } catch (Exception e) {
//            Logger.getLogger(ServicoGenerico.class.getName()).log(Level.SEVERE, null, e);
//
//            System.err.println(e);
        }
        return null;
    }

    private void gravarAnexo(T entity) {

        try {

            Method meth = this.entity.getMethod("getId");
            Long id = (Long) meth.invoke(entity);

            if (id != null) {

                String url = ArquivoUtilitario.REAL_PATH_OPT + entity.getClass().getSimpleName().toLowerCase() + "/";

                if (url != null && !url.equals("")) {
                    url += id + "/";

                    for (String metodo : metodos) {

                        try {
                            meth = this.entity.getMethod(metodo);

                            if (Collection.class.isAssignableFrom(meth.getReturnType())) {

                                for (Anexo anexo : (List<Anexo>) meth.invoke(entity)) {

                                    arquivoUtilitario.gravarAnexo(anexo, url);

                                    if (anexo.getId() != null) {

                                        anexo.setUrl(url);
                                        anexo.setArquivo(null);
                                        anexo.setContent(null);
                                        anexoServico.update(anexo);
                                    }
                                }

                            } else {

                                arquivoUtilitario.gravarAnexo((Anexo) meth.invoke(entity), url);

                                Anexo anexo = (Anexo) meth.invoke(entity);

                                if (anexo.getId() != null) {

                                    anexo.setUrl(url);
                                    anexo.setArquivo(null);
                                    anexo.setContent(null);
                                    anexoServico.update(anexo);
                                }
                            }

                        } catch (NoSuchMethodException e) {
                            System.err.println(this.getClass().getSimpleName() + ": Metodo " + metodo + " nao encontrado. Metodo gravarAnexo, linha: 218.");
                        }
                    }
                }
            }

        } catch (Exception e) {
//            System.err.println(e);
        }
    }
}
